
#include "textura.h"

#include "texels.h"

#include <GL/glut.h>
#include <vector>
#include <cassert>


Textura::Textura(const Texels &texels) {
   cargar(texels);
}

void Textura::cargar(const Texels &texels) {
   // Variable "de prueba" para testear la posibilidad de crear o no la textura
   int ancho = 0;

   //Esto es necesario para manejar Arrays de pixeles que no esten alieados.
   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

   glGenTextures(1, &id);
   glBindTexture(GL_TEXTURE_2D, id);

   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

   glTexImage2D(GL_PROXY_TEXTURE_2D, 
         0, 
         GL_RGB, 
         texels.ancho(), texels.alto(), 
         0, 
         GL_RGB,
         GL_UNSIGNED_BYTE,
         0);

   glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 
         0,
         GL_TEXTURE_WIDTH,
         &ancho);

   if(ancho == 0) {
      id = 0;
      glBindTexture(GL_TEXTURE_2D, 0);
      throw Textura::SinMemoriaExcepcion("No hay suficiente memoria para alocar la textura.");
   }

   glTexImage2D(GL_TEXTURE_2D, 
         0, 
         GL_RGB, 
         texels.ancho(), texels.alto(),
         0, 
         GL_RGB,
         GL_UNSIGNED_BYTE,
         texels.texelsComoArray());

   this->setUso(GL_MODULATE);

   glBindTexture(GL_TEXTURE_2D, 0);
}

void Textura::enlazar() {
   glBindTexture(GL_TEXTURE_2D, id);
}

void Textura::desenlazar() {
   glBindTexture(GL_TEXTURE_2D, 0);
}

void Textura::setMagnificador(int mag) {
   assert(mag == GL_NEAREST or mag == GL_LINEAR);

   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag);
}

void Textura::setMinificador(int min) {
   assert(min == GL_NEAREST or min == GL_LINEAR);

   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min);
}

void Textura::setWrapS(int w) {
   assert(w == GL_REPEAT or w == GL_CLAMP or w == GL_MIRRORED_REPEAT or w == GL_CLAMP_TO_EDGE);

   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, w);
}

void Textura::setWrapT(int w) {
   assert(w == GL_REPEAT or w == GL_CLAMP or w == GL_MIRRORED_REPEAT or w == GL_CLAMP_TO_EDGE);

   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, w);
}

/*
 * Determina como sera usados los colores de la textura.
 * Siendo Cs y As las componentes RGB y Alfa de la textura,
 * siendo Cf y Af las componentes RGB y Alfa del fragmento,
 * siendo C y A las componentes RGB y Alfa finales resultantes,
 * entonces:
 *
 * Si uso == GL_REPLACE:
 *    C = Cs
 *    A = As
 *
 *    Simplemente aplica la textura ignorando el contenido del 
 *    fragmento Cf y Af lo que permite por ejemplo renderizar imagenes
 *    sin que sean afectadas por los colores bases o la iluminacion.
 *
 * Si uso == GL_MODULATE:
 *    C = Cs Cf
 *    A = As Af
 *
 *    Tipicamente usado con Cf = (k, k, k) con k > 0. Esto permite
 *    modular los colores de la textura Cs en funcion del parametro k
 *    Donde k puede depender del color base mas la iluminacion dandole
 *    a la textura un efecto 3d debido a la iluminacion.
 *
 * Si uso == GL_DECAL:
 *    C = Cf (1 - As) + Cs As
 *    A = Af
 *
 *    Es una version modificada de GL_REPLACE. Donde se usa As como
 *    factor para determinar el grado de reemplazo. Con valores As 
 *    cercanos a 1, C = Cs mientras que con As cercanos a 0 C = Cf.
 *
 *
 * Si uso == GL_ADD:
 *    C = Cf + Cs
 *    A = Af As
 *
 **/
void Textura::setUso(int uso) {
   assert(uso == GL_REPLACE or 
         uso == GL_MODULATE or 
         uso == GL_DECAL or 
         uso == GL_ADD);

   glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, uso);
}

Textura::~Textura() {
   if(id) {
      glDeleteTextures(1, &id);
      id = 0;
   }
}

